<?php
class Parser_Html {

    const MIN_CATEGORY_WEIGHT = 3;

    protected $_keywords;

    /**
     *
     * @param string $html
     * @param string $url
     * @return Parser_Html
     */
    public static function factory($html, $url = NULL, $end_url = NULL)
    {
        $url_info = $end_url ? parse_url($end_url) : array();

        $host = str_replace('www.','',Arr::get($url_info, 'host'));

        switch ($host)
        {
            case 'youtube.com':
                $class_name = 'Parser_Html_Youtube';
                break;
            case 'vimeo.com':
                $class_name = 'Parser_Html_Vimeo';
                break;
            case 'edition.cnn.com':
            case 'cnn.com':
                $class_name = 'Parser_Html_Cnn';
                break;
            case 'cracked.com':
                $class_name = 'Parser_Html_Cracked';
                break;
            case 'metacafe.com':
                $class_name = 'Parser_Html_Metacafe';
                break;
            case 'grooveshark.com':
                $class_name = 'Parser_Html_Grooveshark';
                break;
            case 'twitpic.com':
                $class_name = 'Parser_Html_Twitpic';
                break;
            case 'instagr.am':
            case 'instagram.com':
            case 'yfrog.com':
            //case 'pinterest.com':
                $class_name = 'Parser_Html_Imageh';
                break;
            case 'pix.am':
                $class_name = 'Parser_Html_Pixam';
                break;
            case 'screencast.com':
                $class_name = 'Parser_Html_Screencast';
                break;
            case 'soundcloud.com':
                $class_name = 'Parser_Html_Soundcloud';
                break;
            case 'twitch.tv':
            case 'ru.twitch.tv':
            case 'en.twitch.tv':
                $class_name = 'Parser_Html_Twitch';
                break;
            case 'picasaweb.google.com':
                $class_name = 'Parser_Html_Picasa';
                break;
            case 'tinypic.com':
                $class_name = 'Parser_Html_Tinypic';
                break;
            case 'fastpic.ru':
                $class_name = 'Parser_Html_Fastpic';
                break;
			case 'clip.vn':
				$class_name = 'Parser_Html_Clipvn';
				break;
            default:
                $class_name = 'Parser_Html';
        }

        if (preg_match('/^google\.([a-z]{2,3})$/ui', $host))
        {
            $class_name = 'Parser_Html_Google';
        }

        return new $class_name($html, $url_info, $url);
    }


    /**
     *
     * @var array
     */
    protected $_url_info;

    /**
     *
     * @var string
     */
    protected $_url;

    /**
     *
     * @var array
     */
    protected $_fields = array (
        'url' => NULL,
        'title' => 70,
        'description' => 2000,
        'image' => 1000,
        'images' => NULL,
        'type' => NULL,
        'author' => NULL,
        'content' => 1000,
        'category' => NULL,
    );

    /**
     *
     * @var array
     */
    protected $_meta = array();

    /**
     *
     * @var array
     */
    protected $_links = array();

    /**
     *
     * @var DOMDocument
     */
    protected $_dom;

    /**
     *
     * @var DOMXpath
     */
    protected $_xpath;


    /**
     *
     * @var boolean
     */
    protected $_invalid = false;

    /**
     *
     */
    protected $_body;

    /**
     *
     * @param string $html
     * @param string $url_info
     */
    public function __construct($html, $url_info, $url)
    {
        $this->_url_info = $url_info;
        $this->_url = $url;
        $this->_body = $html;
        try {
            $old_libxml_error = libxml_use_internal_errors(true);
            $doc = new DOMDocument('1.0', 'utf-8');
            if ($doc->loadHTML($html))
            {
                libxml_use_internal_errors($old_libxml_error);
                $this->_dom = $doc;
            }
            $this->_xpath = new DOMXpath($this->_dom);

            $meta = $this->select('meta');

            foreach ($meta as $tag)
            {
                if (isset($tag['attributes']['name']) && isset($tag['attributes']['content']))
                {
                    $this->_meta[$tag['attributes']['name']] = $tag['attributes']['content'];
                }
                if (isset($tag['attributes']['property']) && isset($tag['attributes']['content']))
                {
                    $this->_meta[$tag['attributes']['property']] = $tag['attributes']['content'];
                }
            }

            $links = $this->select('link');

            foreach ($links as $tag)
            {
                if (isset($tag['attributes']['rel']) && isset($tag['attributes']['href']))
                {
                    $this->_links[$tag['attributes']['rel']] = $tag['attributes']['href'];
                }
            }

        } catch (Exception $e) {
            $this->_invalid = TRUE;
        }
    }

    /**
     *
     * @param string $key
     * @return string|NULL
     */
    protected function _meta($key)
    {
        return Arr::get($this->_meta, $key);
    }

    /**
     *
     * @param string $rel
     * @return string|NULL
     */
    protected function _link($rel)
    {
        return Arr::get($this->_links, $rel);
    }

    /**
     *
     * @return string|FALSE
     */
    public function need_redirect()
    {
        return FALSE;
    }

    /**
     *
     * @return array
     */
    public function parse()
    {
        if ($this->_invalid)
        {
            return array (
                'type' => 'unknown',
                'url'  => $this->_url
            );
        }

        $this->_result = array();
        foreach ($this->_fields as $field => $len)
        {
            $value = $this->{$field}();
            if ( ! empty($value))
            {
                $this->_result[$field] = is_string($value) ? strip_tags($value) : $value;
                if ($len)
                {
                    $this->_result[$field] = strip_tags(text::limit_chars($this->_result[$field], $len));
                }
            }
        }



        return $this->_result;
    }

    public function url()
    {
        return $this->_url;
    }


    public function title()
    {
        $value = $this->_meta('og:title');
        if (empty($value))
        {
            $value = $this->select('title');
            $value = isset($value[0]) ? Arr::get($value[0], 'text') : null;
        }
        return $value;
    }

    public function description()
    {
        $value = $this->_meta('og:description');
        if (empty($value))
        {
            $value = $this->_meta('description');
        }

        return $value;
    }

    public function image()
    {
        if (Arr::get($this->_result, 'image'))
        {
            return $this->_result['image'];
        }

        $value = $this->_meta('og:image');

        if (empty($value))
        {
            $value = $this->_link('image_src');
        }

        if (substr($value, 0, 2)=='//')
        {
            $value = $this->_url_info['scheme'] . ':' . $value;
        }
        return $value;
    }

    public function images()
    {
        $result = array();
        if (empty($this->_result['image']))
        {
            $imgs = $this->select('img');
            foreach ($imgs as $item)
            {
                $image = Arr::get($item['attributes'], 'src');
                if ( ! $image)
                {
                    continue;
                }
                if (substr($image, 0, 4) != 'http')
                {
                    if ($image && $image{0} == '.')
                    {
                        $image = $this->_relative_path($image);
                    }
                    else
                    {
                        if (substr($image, 0, 2) == '//')
                        {
                            $image = 'http:' . $image;
                        }
                        elseif(strpos($image, 'data:image/') === 0)
                        {
                            continue;
                        }
                        else
                        {
                            $image = Arr::get($this->_url_info, 'scheme') . ':///' . Arr::get($this->_url_info, 'host') . '/' . $image;
                            $image = str_replace('//', '/', $image);
                        }
                    }
                }
                array_push($result, $image);
            }
        }
        return array_values(array_unique($result));
    }

    public function type()
    {
        $type = $this->_meta('og:type');
        if ($type != 'image' && $type != 'video')
        {
            $type = 'link';
        }

        if ($video = $this->_meta('og:video'))
        {
            if ( ! empty($video))
            {
                $type = 'video';
            }
        }

        if ($type=='video')
        {
            $content = $this->content();

            if (empty($content))
            {
                $type = 'link';
            }
        }

        return $type;
    }

    public function author()
    {

    }

    public function content()
    {
        $content = $this->_meta('og:video');
        if ( empty($content))
        {
            $content = $this->_link('video_src');
        }
        return $content;
    }

    public function category()
    { 
		return;
        $text = mb_strtolower(strip_tags($this->_body));
        list($id, $weight, $words) = Service_Category::category_id_by_text($text);
        $this->_keywords = $words;
        if ($weight >= self::MIN_CATEGORY_WEIGHT)
        {
            return $id;
        }
    }

    public function get_keywords()
    {
        return $this->_keywords;
    }


    /**
     * @param string $selector
     * @param bool $as_array
     */
    public function select($selector, $as_array = true)
    {
        $elements = $this->_xpath->evaluate($this->_selector_to_xpath($selector));
        return $as_array ? $this->_elements_to_array($elements) : null;
    }


    protected function _relative_path($path)
    {
        $url = $this->_url;
        if (strpos($url, '?'))
        {
            $url = strstr($url, '?', TRUE);
        }


        if (substr($path, 0, 2) == '..')
        {
            if (substr($url, -1) == '/')
            {
                $path = $url.$path;
            }
            else
            {
                $search = strrchr($url, '/');
                $replace = "/$path";
                $path = str_replace($search, $replace, $url);
            }
        }
        else
        {
            if (substr($url, -1) == '/')
            {
                $path = $url.ltrim($path, "./");
            }
            else
            {
                $search = strrchr($url, '/');
                $replace = substr($path, 1);
                $path = str_replace($search, $replace, $url);
            }
        }
        return $path;
    }


    protected function _selector_to_xpath($selector)
    {
        $selector = 'descendant-or-self::' . $selector;
        // ,
        $selector = preg_replace('/\s*,\s*/', '|descendant-or-self::', $selector);
        // :button, :submit, etc
        $selector = preg_replace('/:(button|submit|file|checkbox|radio|image|reset|text|password)/', 'input[@type="\1"]', $selector);
        // [id]
        $selector = preg_replace('/\[(\w+)\]/', '*[@\1]', $selector);
        // foo[id=foo]
        $selector = preg_replace('/\[(\w+)=[\'"]?(.*?)[\'"]?\]/', '[@\1="\2"]', $selector);
        // [id=foo]
        $selector = str_replace(':[', ':*[', $selector);
        // div#foo
        $selector = preg_replace('/([\w\-]+)\#([\w\-]+)/', '\1[@id="\2"]', $selector);
        // #foo
        $selector = preg_replace('/\#([\w\-]+)/', '*[@id="\1"]', $selector);
        // div.foo
        $selector = preg_replace('/([\w\-]+)\.([\w\-]+)/', '\1[contains(@class,"\2")]', $selector);
        // .foo
        $selector = preg_replace('/\.([\w\-]+)/', '*[contains(@class,"\1")]', $selector);
        // div:first-child
        $selector = preg_replace('/([\w\-]+):first-child/', '*/\1[position()=1]', $selector);
        // div:last-child
        $selector = preg_replace('/([\w\-]+):last-child/', '*/\1[position()=last()]', $selector);
        // :first-child
        $selector = str_replace(':first-child', '*/*[position()=1]', $selector);
        // :last-child
        $selector = str_replace(':last-child', '*/*[position()=last()]', $selector);
        // div:nth-child
        $selector = preg_replace('/([\w\-]+):nth-child\((\d+)\)/', '*/\1[position()=\2]', $selector);
        // :nth-child
        $selector = preg_replace('/:nth-child\((\d+)\)/', '*/*[position()=\1]', $selector);
        // :contains(Foo)
        $selector = preg_replace('/([\w\-]+):contains\((.*?)\)/', '\1[contains(string(.),"\2")]', $selector);
        // >
        $selector = preg_replace('/\s*>\s*/', '/', $selector);
        // ~
        $selector = preg_replace('/\s*~\s*/', '/following-sibling::', $selector);
        // +
        $selector = preg_replace('/\s*\+\s*([\w\-]+)/', '/following-sibling::\1[position()=1]', $selector);
        // ' '
        $selector = preg_replace('/\s+/', '/descendant::', $selector);
        $selector = str_replace(']*', ']', $selector);
        $selector = str_replace(']/*', ']', $selector);
        return $selector;
    }

    protected function _elements_to_array($elements)
    {
        $array = array();
        for ($i = 0, $length = $elements->length; $i < $length; ++$i)
        {
            if ($elements->item($i)->nodeType == XML_ELEMENT_NODE)
            {
                array_push($array, $this->_element_to_array($elements->item($i)));
            }
        }
        return $array;
    }

    protected function _element_to_array($element) {
        $array = array(
            'name' => $element->nodeName,
            'attributes' => array(),
            'text' => $element->textContent,
            'children' => $this->_elements_to_array($element->childNodes)
        );
        if ($element->attributes->length)
        {
            foreach($element->attributes as $key => $attr)
            {
                $array['attributes'][$key] = $attr->value;
            }
        }
        return $array;
    }

}